//======== Programming Assignment 2: Deadlock Scenario ======================
//===========================================================================
//===========================================================================
//===========================================================================
//===========================================================================
#include <stdio.h>
#include <iostream.h>
#include <time.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>
#include <sync.h>
#include <sys/siginfo.h>
#include <sys/neutrino.h>
#include <sys/netmgr.h>
#include <sys/syspage.h> 
//=============================================================================

pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;

#define PCnt 10 /* Maximum number of threads*/
#define eps .005 
float priority[PCnt]={0}; // priority of threads 
int active_p=0;			  // detemine the active threas that should be run	


void ThreadManager();



//=========================== Use the timer class that you developed for programming assignment 1   ===========================
class Timer
{
public:
	int channel_id;
	
	Timer(){
		if ((channel_id = ChannelCreate (0)) == -1) {
			perror (NULL);
			return;
		}
	}
	void wait()
	{
		MsgReceive (channel_id, NULL, NULL, NULL);
	
	}
	
	void setup(unsigned int seconds)
	{
		timer_t    timerid;    
		sigevent   event;     
	    itimerspec timer;  
	    int        coid;     
	     
		// connection back to the previous channel created
	    coid = ConnectAttach (0, 0, channel_id, 0, 0);
	     
	    if (coid == -1) {
	    	printf("Error coid");
	    	perror (NULL);
	    	return;
	    	//return 0;
	    }
		     
		// macro to create a pulse event
		SIGEV_PULSE_INIT (&event, coid, SIGEV_PULSE_PRIO_INHERIT, 1, 0);
		  
		//creating the timer object
		if (timer_create (CLOCK_REALTIME, &event, &timerid) == -1) {
		    perror(NULL);
		    printf("error"); 
		    return;
		    //return(0);
		}
	     
		// configuring the timer with the arguments
		timer.it_value.tv_sec = seconds;
		timer.it_value.tv_nsec = 0;
		timer.it_interval.tv_sec = seconds;
		timer.it_interval.tv_nsec = 0;

		// finally, we start it
		timer_settime (timerid, 0, &timer, NULL);
	}
};



//============================================  develope the mutex class here ====================================================
class CS  // The developed Mutex class 
{
	private:
		int s;								// mutex status; 0 is free 1 is taken
	public:
		CS ();
		~CS();
		void lock(int p, CS sp[]);
		void unlock(int p);
};

CS::CS() {
	s=0;    
}

CS::~CS(){
}


void CS::lock(int p, CS sp[]){
	cout<< "LOCKed by P"<<p;	 // display a message
}

void CS::unlock(int p){
	cout<<"UNLOCKed by P"<<p;	 // display a message
}


//=============================================================================

//=============================================================================


CS s[PCnt]; // mutexes are instantiated here 

//=============================================================================
void * P1(void* arg)
{
	int cnt=1,j;
	//Message msg;
	//cout << ".........P1 thread starts.........";
	while(1){
		pthread_mutex_lock(&mutex);
		do{
			pthread_cond_wait(&cond,&mutex); // check for a message from ThreadManager
		}while(active_p!=1);				 // check the active thread	

		//if(active_p==1)     				 // check the active thread	
		{
		//+++++++++++++++++++++++++++++++++++++++++++++
			cout<<"P1->";//<<cnt<<endl;
			if(cnt==1){
				cout << ".....CS1 ..";
				s[1].lock(1,s);
			}
			else if(cnt==2){
				cout << ".....CS2 ..";
				s[2].lock(1,s);
			}
			else if(cnt==3){
				cout << ".....CS2 ..";
				s[2].unlock(1);
			} 
			else if(cnt==4){
				cout << ".....CS1 ..";
				s[1].unlock(1);
			}
			else if (cnt == 6){
				//Message msg;
				cout << ".........P1 thread ends.........";
				priority[1]=0; // to remove process 1 from the queue of ThreadManager
				pthread_mutex_unlock(&mutex);
				break;
			}			
			cnt++;
		}
		//---------------------------------------------
		//pthread_cond_wait(&cond,&mutex); // check for a message from ThreadManager
		pthread_mutex_unlock(&mutex);
	}

}
//=============================================================================
void * P2(void* arg)
{

	int cnt=1,j;
	//Message msg;
	//cout << ".........P2 thread starts.........";

	while(1){
		pthread_mutex_lock(&mutex);
		do{
			pthread_cond_wait(&cond,&mutex); // check for a message from ThreadManager
		}while(active_p!=2);				 // check the active thread	

		//if(active_p==2)     				 // check the active thread	
		{
		//+++++++++++++++++++++++++++++++++++++++++++++
			cout<<"P2->";//<<cnt<<endl;
			if(cnt==1){
				cout << ".....CS2 ..";
				s[2].lock(2,s);
			}
			else if(cnt==2){
				cout << ".....CS1 ..";
				s[1].lock(2,s);
			}
			else if(cnt==3){
				cout << ".....CS1 ..";
					s[1].unlock(2);
			} 
			else if(cnt==4){
				cout << ".....CS2 ..";
				s[2].unlock(2);
			}
			else if (cnt == 6){
				//Message msg;
				cout << ".........P2 thread ends.........";
				priority[2]=0; // to remove process 2 from the queue of ThreadManager
				pthread_mutex_unlock(&mutex);
				break;
			}			
			cnt++;
		}
		//---------------------------------------------
		//pthread_cond_wait(&cond,&mutex); // check for a message from ThreadManager
		pthread_mutex_unlock(&mutex);
	}
}


void ThreadManager(){ // determines that which thread should be run
	float p;	
	int i;

	pthread_mutex_lock(&mutex);

		p=-1;
		for(i=1;i<PCnt;i++){ // find the thread with the most priority and set it as active thread
			if(priority[i]>p){
				active_p=i;
				p=priority[i];
			}
		}
		pthread_mutex_unlock(&mutex);
		pthread_cond_broadcast(&cond); // send the signal to the threads
}

//=============================================================================
//                                 M     A     I   N
//=============================================================================
int main(void)
{
	pthread_t P1_ID,P2_ID;       //p1, p2, threads
	int cnt=0,i;
	
	#ifdef PRIORITY_CEILING
	//set the priority ceiling of the mutexex

//	s[1].PC=??; 
//	s[2].PC=??;
	
	#endif


	//creating up a periodic  timer to generate pulses every 1 sec.
	Timer t;
	t.setup(1);
	while(1)
	{
		//--------------------------------------------		
		pthread_mutex_lock(&mutex);
		 // release P2 at t=0
		if(cnt==0){ 
			priority[2]=.5;
			pthread_create(&P2_ID , NULL, P2, NULL);
		}
		 // release P1 at t=2
		else if(cnt==2){
			priority[1]=.7;
			pthread_create(&P1_ID , NULL, P1, NULL);
		}
		 // terminate the program at t=30
		else if (cnt == 30){
			break;
		}			
		pthread_mutex_unlock(&mutex);
		//---------------------------------------------
		// waite for the timer pulse
		t.wait();
		//+++++++++++++++++++++++++++++++++++++++++++++
		cnt++;
		// message
		cout<<endl<<"tick="<<cnt<</*", active_p="<<active_p<<*/"->";//<<endl;

		ThreadManager(); // to find out and run the active thread

    	//*********************************************
	}
}
//=============================================================================

